/*
 * z64
 *
 * This program is free software; you can redistribute it and/
 * or modify it under the terms of the GNU General Public Li-
 * cence as published by the Free Software Foundation; either
 * version 2 of the Licence, or any later version.
 *
 * This program is distributed in the hope that it will be use-
 * ful, but WITHOUT ANY WARRANTY; without even the implied war-
 * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public Licence for more details.
 *
 * You should have received a copy of the GNU General Public
 * Licence along with this program; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
 * USA.
 *
**/

#include "rsp_opinfo.h"

static const int vector_elements_2[16][8] =
{
	{ 0, 1, 2, 3, 4, 5, 6, 7 },		// none
	{ 0, 1, 2, 3, 4, 5, 6, 7 },		// ???
	{ 0, 0, 2, 2, 4, 4, 6, 6 },		// 0q
	{ 1, 1, 3, 3, 5, 5, 7, 7 },		// 1q
	{ 0, 0, 0, 0, 4, 4, 4, 4 },		// 0h
	{ 1, 1, 1, 1, 5, 5, 5, 5 },		// 1h
	{ 2, 2, 2, 2, 6, 6, 6, 6 },		// 2h
	{ 3, 3, 3, 3, 7, 7, 7, 7 },		// 3h
	{ 0, 0, 0, 0, 0, 0, 0, 0 },		// 0
	{ 1, 1, 1, 1, 1, 1, 1, 1 },		// 1
	{ 2, 2, 2, 2, 2, 2, 2, 2 },		// 2
	{ 3, 3, 3, 3, 3, 3, 3, 3 },		// 3
	{ 4, 4, 4, 4, 4, 4, 4, 4 },		// 4
	{ 5, 5, 5, 5, 5, 5, 5, 5 },		// 5
	{ 6, 6, 6, 6, 6, 6, 6, 6 },		// 6
	{ 7, 7, 7, 7, 7, 7, 7, 7 },		// 7
};

void rsp_get_opinfo(UINT32 op, rsp_opinfo_t * info)
{
  int op2;
  int i;
  info->op = op;
  switch (op>>26) {
    case 0:     /* SPECIAL */
      op2 = RSP_SPECIAL_OFFS + (op&0x3f);
      break;
    case 0x12:	/* COP2 */
      if (((op>>21)&0x1f) >= 0x10)
        op2 = RSP_COP2_2_OFFS + (op & 0x3f);
      else
        op2 = RSP_COP2_1_OFFS + ((op >> 21) & 0x1f);
      break;
    case 0x32:	/* LWC2 */
      op2 = RSP_LWC2_OFFS + ((op>>11)&0x1f);
      break;
    case 0x3a:	/* SWC2 */
      op2 = RSP_SWC2_OFFS + ((op>>11)&0x1f);
      break;
    default:
      op2 = RSP_BASIC_OFFS + (op>>26);
      if (op2 == RSP_REGIMM) {
        switch (RTREG)
        {
          case 0x00:	/* BLTZ */
            op2 = RSP_BLTZ;
            break;
          case 0x01:	/* BGEZ */
            op2 = RSP_BGEZ;
            break;
          case 0x11:	/* BGEZAL */
            op2 = RSP_BGEZAL;
            break;
        }
      }
  }
  info->op2 = op2;

  memset(&info->used, 0, sizeof(info->used));
  memset(&info->set, 0, sizeof(info->set));
  info->used.accu = info->used.flag = 0;
  info->set.accu = info->set.flag = 0;
  info->flags = 0;

	int dest = (op >> 16) & 0x1f;
	int base = (op >> 21) & 0x1f;
	int index = (op >> 7) & 0xf;
	int offset = (op & 0x7f);
	if (offset & 0x40)
		offset |= 0xffffffc0;

  switch(op2) {
    case RSP_SPECIAL:
    case RSP_BLTZ:
      info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_COND | RSP_OPINFO_USEPC;
      break;
    case RSP_BGEZ:
      info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_COND | RSP_OPINFO_USEPC;
      break;
    case RSP_BGEZAL:
      info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_COND | RSP_OPINFO_LINK | RSP_OPINFO_USEPC;
      break;
    case RSP_J:
      info->flags = RSP_OPINFO_JUMP;
      break;
    case RSP_JAL:
      info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_LINK | RSP_OPINFO_USEPC;
      break;
    case RSP_BEQ:
    case RSP_BNE:
    case RSP_BLEZ:
    case RSP_BGTZ:
      info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_COND | RSP_OPINFO_USEPC;
      break;
    case RSP_ADDI:
    case RSP_ADDIU:
    case RSP_SLTI:
    case RSP_SLTIU:
    case RSP_ANDI:
    case RSP_ORI:
    case RSP_XORI:
    case RSP_LUI:
    case RSP_COP0:
    case RSP_LB:
    case RSP_LH:
    case RSP_LW:
    case RSP_LBU:
    case RSP_LHU:
    case RSP_SB:
    case RSP_SH:
    case RSP_SW:
      break;

    case RSP_SLL:
    case RSP_SRL:
    case RSP_SRA:
    case RSP_SLLV:
    case RSP_SRLV:
    case RSP_SRAV:
      break;
      
    case RSP_JR:
      info->flags = RSP_OPINFO_JUMP;
      break;
    case RSP_JALR:
      info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_LINK | RSP_OPINFO_USEPC;
      break;
    case RSP_BREAK:
      info->flags = RSP_OPINFO_BREAK;
      break;
      
    case RSP_ADD:
    case RSP_ADDU:
    case RSP_SUB:
    case RSP_SUBU:
    case RSP_AND:
    case RSP_OR:
    case RSP_XOR:
    case RSP_NOR:
    case RSP_SLT:
    case RSP_SLTU:
      break;

    case RSP_MFC2:
    {
      int el = op >> 7;
      RSP_SET_VEC_I(info->used, VS1REG, ((el+0)&0xf)>>1);
      RSP_SET_VEC_I(info->used, VS1REG, ((el+1)&0xf)>>1);
      break;
    }
    case RSP_CFC2:
      RSP_SET_FLAG_I(info->used, RDREG & 3);
      break;
    case RSP_MTC2:
    {
      int el = op >> 7;
      RSP_SET_VEC_I(info->set, VS1REG, ((el+0)&0xf)>>1);
      RSP_SET_VEC_I(info->set, VS1REG, ((el+1)&0xf)>>1);
      break;
    }
    case RSP_CTC2:
      RSP_SET_FLAG_I(info->set, RDREG & 3);
      break;


    case RSP_LBV:
      RSP_SET_VEC_I(info->set, dest, index>>1);
      break;
    case RSP_LSV:
      for (i=index; i<index+2; i++)
        RSP_SET_VEC_I(info->set, dest, (i>>1)&7);
      break;
    case RSP_LLV:
      for (i=index; i<index+4; i++)
        RSP_SET_VEC_I(info->set, dest, (i>>1)&7);
      break;
    case RSP_LDV:
      for (i=index; i<index+8; i++)
        RSP_SET_VEC_I(info->set, dest, (i>>1)&7);
      break;
    case RSP_LQV:
    case RSP_LRV:
      // WARNING WARNING WARNING
      // we assume this instruction always used to load the full vector
      // i.e. the address is always 16 bytes aligned
//       for (i=0; i<8; i++)
//         RSP_SET_VEC_I(info->set, dest, i);
      break;
    case RSP_LPV:
    case RSP_LUV:
    case RSP_LHV:
    case RSP_LWV:
      for (i=0; i<8; i++)
        RSP_SET_VEC_I(info->set, dest, i);
      break;
    case RSP_LFV:
      for (i=(index>>1); i<(index>>1)+4; i++)
        RSP_SET_VEC_I(info->set, dest, i);
      break;
    case RSP_LTV:
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 110010 | BBBBB | TTTTT | 01011 | IIII | Offset |
			// --------------------------------------------------
			//
			// Loads one element to maximum of 8 vectors, while incrementing element index

			// FIXME: has a small problem with odd indices

			int element;
			int vs = dest;
			int ve = dest + 8;
			if (ve > 32)
				ve = 32;

			element = 7 - (index >> 1);

			if (index & 1)	fatalerror("RSP: LTV: index = %d\n", index);

			for (i=vs; i < ve; i++)
			{
				element = ((8 - (index >> 1) + (i-vs)) << 1);
				RSP_SET_VEC_I(info->set, i, (element & 0xf)>>1);
				RSP_SET_VEC_I(info->set, i, ((element+1) & 0xf)>>1);
			}
			break;
		}

    case RSP_SBV:
      RSP_SET_VEC_I(info->used, dest, index>>1);
      break;
    case RSP_SSV:
      for (i=index; i<index+2; i++)
        RSP_SET_VEC_I(info->used, dest, (i>>1)&7);
      break;
    case RSP_SLV:
      for (i=index; i<index+4; i++)
        RSP_SET_VEC_I(info->used, dest, (i>>1)&7);
      break;
    case RSP_SDV:
      for (i=index; i<index+8; i++)
        RSP_SET_VEC_I(info->used, dest, (i>>1)&7);
      break;
    case RSP_SQV:
    case RSP_SRV:
      // WARNING WARNING WARNING
      // we assume this instruction always used to store the full vector
      // i.e. the address is always 16 bytes aligned
      for (i=0; i<8; i++)
        RSP_SET_VEC_I(info->used, dest, i);
      break;
    case RSP_SPV:
    case RSP_SUV:
    case RSP_SHV:
    case RSP_SWV:
      for (i=0; i<8; i++)
        RSP_SET_VEC_I(info->used, dest, i);
      break;
    case RSP_SFV:
      for (i=(index>>1); i<(index>>1)+4; i++)
        RSP_SET_VEC_I(info->used, dest, i);
      break;
    case RSP_STV:
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 111010 | BBBBB | TTTTT | 01011 | IIII | Offset |
			// --------------------------------------------------
			//
			// Stores one element from maximum of 8 vectors, while incrementing element index

			int element, eaoffset;
			int vs = dest;
			int ve = dest + 8;
			if (ve > 32)
				ve = 32;

			element = 8 - (index >> 1);
			if (index & 0x1)	fatalerror("RSP: STV: index = %d at %08X\n", index, rsp.ppc);

			for (i=vs; i < ve; i++)
			{
				RSP_SET_VEC_I(info->used, i, element & 0x7);
				element++;
			}
			break;
		}

    case RSP_VMULF:
    case RSP_VMULU:
    case RSP_VMUDL:
    case RSP_VMUDM:
    case RSP_VMUDN:
    case RSP_VMUDH:
		{
			for (i=0; i < 8; i++)
			{
				int sel = VEC_EL_2(EL, i);
        RSP_SET_VEC_I(info->used, VS1REG, i);
        RSP_SET_VEC_I(info->used, VS2REG, sel);
        RSP_SET_VEC_I(info->set, VDREG, i);
        RSP_SET_ACCU_I(info->set, i, 14);
      }
      break;
    }
    case RSP_VMACF:
    case RSP_VMACU:
    case RSP_VMADL:
    case RSP_VMADM:
    case RSP_VMADN:
    case RSP_VMADH:
		{
			for (i=0; i < 8; i++)
			{
				int sel = VEC_EL_2(EL, i);
        RSP_SET_VEC_I(info->used, VS1REG, i);
        RSP_SET_VEC_I(info->used, VS2REG, sel);
        RSP_SET_VEC_I(info->set, VDREG, i);
        RSP_SET_ACCU_I(info->used, i, 14);
        RSP_SET_ACCU_I(info->set, i, 14);
      }
      break;
    }
    case RSP_VADD:
    case RSP_VSUB:
		{
			for (i=0; i < 8; i++)
			{
				int sel = VEC_EL_2(EL, i);
        RSP_SET_VEC_I(info->used, VS1REG, i);
        RSP_SET_VEC_I(info->used, VS2REG, sel);
        RSP_SET_VEC_I(info->set, VDREG, i);
        RSP_SET_ACCU_I(info->set, i, 2);
      }
      RSP_SET_FLAG_I(info->used, 0);
      RSP_SET_FLAG_I(info->set, 0);
      break;
    }
    case RSP_VABS:
		{
			for (i=0; i < 8; i++)
			{
				int sel = VEC_EL_2(EL, i);
        RSP_SET_VEC_I(info->used, VS1REG, i);
        RSP_SET_VEC_I(info->used, VS2REG, sel);
        RSP_SET_VEC_I(info->set, VDREG, i);
        RSP_SET_ACCU_I(info->set, i, 2);
      }
      break;
    }
    case RSP_VADDC:
    case RSP_VSUBC:
		{
			for (i=0; i < 8; i++)
			{
				int sel = VEC_EL_2(EL, i);
        RSP_SET_VEC_I(info->used, VS1REG, i);
        RSP_SET_VEC_I(info->used, VS2REG, sel);
        RSP_SET_VEC_I(info->set, VDREG, i);
        RSP_SET_ACCU_I(info->set, i, 2);
      }
      RSP_SET_FLAG_I(info->set, 0);
      break;
    }
    case RSP_VSAW:
			switch (EL)
			{
				case 0x08:		// VSAWH
				{
					for (i=0; i < 8; i++)
					{
            RSP_SET_VEC_I(info->set, VDREG, i);
            RSP_SET_ACCU_I(info->used, i, 8);
					}
					break;
				}
				case 0x09:		// VSAWM
				{
					for (i=0; i < 8; i++)
					{
            RSP_SET_VEC_I(info->set, VDREG, i);
            RSP_SET_ACCU_I(info->used, i, 4);
					}
          break;
				}
				case 0x0a:		// VSAWL
				{
					for (i=0; i < 8; i++)
					{
            RSP_SET_VEC_I(info->set, VDREG, i);
            RSP_SET_ACCU_I(info->used, i, 2);
					}
					break;
				}
				default:	fatalerror("RSP: VSAW: el = %d\n", EL);
			}
			break;
    case RSP_VLT:
		{
			for (i=0; i < 8; i++)
			{
				int sel = VEC_EL_2(EL, i);
        RSP_SET_VEC_I(info->used, VS1REG, i);
        RSP_SET_VEC_I(info->used, VS2REG, sel);
        RSP_SET_VEC_I(info->set, VDREG, i);
        RSP_SET_ACCU_I(info->set, i, 2);
      }
      RSP_SET_FLAG_I(info->set, 0);
      RSP_SET_FLAG_I(info->set, 1);
      break;
    }
    case RSP_VEQ:
    case RSP_VNE:
    case RSP_VGE:
		{
			for (i=0; i < 8; i++)
			{
				int sel = VEC_EL_2(EL, i);
        RSP_SET_VEC_I(info->used, VS1REG, i);
        RSP_SET_VEC_I(info->used, VS2REG, sel);
        RSP_SET_VEC_I(info->set, VDREG, i);
        RSP_SET_ACCU_I(info->set, i, 2);
      }
      RSP_SET_FLAG_I(info->used, 0);
      RSP_SET_FLAG_I(info->set, 0);
      RSP_SET_FLAG_I(info->set, 1);
      break;
    }
    case RSP_VCL:
		{
			for (i=0; i < 8; i++)
			{
				int sel = VEC_EL_2(EL, i);
        RSP_SET_VEC_I(info->used, VS1REG, i);
        RSP_SET_VEC_I(info->used, VS2REG, sel);
        RSP_SET_VEC_I(info->set, VDREG, i);
        RSP_SET_ACCU_I(info->set, i, 2);
      }
      RSP_SET_FLAG_I(info->used, 0);
      RSP_SET_FLAG_I(info->used, 1);
      RSP_SET_FLAG_I(info->set, 0);
      RSP_SET_FLAG_I(info->set, 1);
      RSP_SET_FLAG_I(info->set, 2);
      break;
    }
    case RSP_VCH:
    case RSP_VCR:
		{
			for (i=0; i < 8; i++)
			{
				int sel = VEC_EL_2(EL, i);
        RSP_SET_VEC_I(info->used, VS1REG, i);
        RSP_SET_VEC_I(info->used, VS2REG, sel);
        RSP_SET_VEC_I(info->set, VDREG, i);
        RSP_SET_ACCU_I(info->set, i, 2);
      }
      RSP_SET_FLAG_I(info->set, 0);
      RSP_SET_FLAG_I(info->set, 1);
      RSP_SET_FLAG_I(info->set, 2);
      break;
    }
    case RSP_VMRG:
		{
			for (i=0; i < 8; i++)
			{
				int sel = VEC_EL_2(EL, i);
        RSP_SET_VEC_I(info->used, VS1REG, i);
        RSP_SET_VEC_I(info->used, VS2REG, sel);
        RSP_SET_VEC_I(info->set, VDREG, i);
        RSP_SET_ACCU_I(info->set, i, 2);
      }
      RSP_SET_FLAG_I(info->used, 1);
      break;
    }
    case RSP_VAND:
    case RSP_VNAND:
    case RSP_VOR:
    case RSP_VNOR:
    case RSP_VXOR:
    case RSP_VNXOR:
		{
			for (i=0; i < 8; i++)
			{
				int sel = VEC_EL_2(EL, i);
        RSP_SET_VEC_I(info->used, VS1REG, i);
        RSP_SET_VEC_I(info->used, VS2REG, sel);
        RSP_SET_VEC_I(info->set, VDREG, i);
        RSP_SET_ACCU_I(info->set, i, 2);
      }
      break;
    }
    case RSP_VRCP:
    case RSP_VRCPL:
    case RSP_VRCPH:
    case RSP_VRSQL:
    case RSP_VRSQH:
		{
			int del = (VS1REG & 7);
			int sel = VEC_EL_2(EL, del);
      
      RSP_SET_VEC_I(info->used, VS2REG, sel);
      
			for (i=0; i < 8; i++)
			{
				int element = VEC_EL_2(EL, i);
        RSP_SET_VEC_I(info->used, VS2REG, element);
        RSP_SET_ACCU_I(info->set, i, 2);
			}

      RSP_SET_VEC_I(info->set, VDREG, del);
			break;
		}
    case RSP_VMOV:
		{
			int element = VS1REG & 7;
      RSP_SET_VEC_I(info->used, VS2REG, VEC_EL_2(EL, 7-element));
      RSP_SET_VEC_I(info->set, VDREG, element);
			break;
		}

    default:
    {
//       char string[200];
//       rsp_dasm_one(string, 0x800, op);
//       if (strcmp(string, "???")) {
//         printf("%s\n", string);
//         printf("unimplemented opcode\n");
//       }
      break;
    }
  }
}
